/*
 * GClubCupHistoryRecord.java
 * Created on 23 août 2008
 *
 * Glacéo Internet Platform
 * http://sourceforge.net/projects/chm/
 *
 * Copyright (c) 2005-2008, Jens and Jan Janke (VirtualHockey Project)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.hockeo.vhbase.data.dao.club;


import java.math.BigDecimal;
import java.math.RoundingMode;

import javax.persistence.Column;
import javax.persistence.DiscriminatorValue;
import javax.persistence.Entity;

import com.hockeo.vhbase.data.dao.IGEntity;
import com.hockeo.vhbase.data.dao.contest.GContestRound;
import com.hockeo.vhbase.data.dao.contest.GKO3ContestElement;
import com.hockeo.vhbase.data.dao.match.GKOMatch;
import com.hockeo.vhbase.data.dao.season.GSeason;
import com.hockeo.vhbase.data.dao.standings.GECRStandings;
import com.hockeo.vhbase.data.dao.standings.GENRStandings;
import com.hockeo.vhbase.data.helper.GClubMultiRoundContestStatus;
import com.hockeo.vhbase.data.helper.GContestRoundFormat;
import com.hockeo.vhbase.data.helper.GContestRoundType;
import com.hockeo.vhbase.data.helper.GMatchResult;

/**
 * Records a club's results during a normal national cup season.
 *
 * @version $Id$
 * @author jjanke
 */
@Entity
@DiscriminatorValue( "CUP" )
public class GClubCupHistoryRecord extends GClubAbstractCupHistoryRecord
{
  @Column( name = "nc_first_round" )
  private int d_nFirstRound = 0;

  @Column( name = "nc_last_round" )
  private int d_nLastRound  = 0;

  /** Default parameterless constructor. */
  protected GClubCupHistoryRecord()
  {}

  /**
   * Constructs a new GClubCupHistoryRecord.
   *
   * @param season the season for which to create the record
   * @param club the club to attache to the created record
   * @param round the (cup) round to which the club is initially assigned
   */
  public GClubCupHistoryRecord( GSeason season, GClub club, GContestRound round )
  {
    super( season, club, round );

    assignToRound( round );
  }

  /**
   * Calls the super class' method {@link #reset()} and resets cup specific fields in
   * addition.
   */
  @Override
  public void reset()
  {
    super.reset();

    d_nFirstRound = 0;
    d_nLastRound = 0;
  }

  @Override
  public GClubMultiRoundContestStatus getMultiRoundContestStatus()
  {
    if ( getReachedRound() == GContestRoundType.WINNER )
      return GClubMultiRoundContestStatus.FINISHED_FIRST;

    if ( getNumLostKOSeries() > 0 )
      if ( getReachedRound() == GContestRoundType.FINAL )
        return GClubMultiRoundContestStatus.REACHED_LAST_ROUND;
      else
        return GClubMultiRoundContestStatus.ELIMINATED;

    return GClubMultiRoundContestStatus.QUALIFIED;
  }

  @Override
  public void updateECRStandings( GECRStandings ecr )
  {
    ecr.setPointsS1Cup( getPointsECR() );
  }

  @Override
  public void updateENRStandings( GENRStandings enr )
  {
  // ENR points are not obtained during a national cup contest
  }

  /**
   * Adds the results of the given KO match which has been played as part of the also
   * given contest element.
   *
   * @param element the element in which a KO match has been played
   * @param komatch the played KO match
   */
  public void addRoundResult( GKO3ContestElement element, GKOMatch komatch )
  {
    if ( !komatch.isPlayed() )
      throw new IllegalArgumentException( "The KOMatch " + komatch.getId() + " has not yet been played." );

    if ( !element.getContest().equals( getContest() ) )
      throw new IllegalArgumentException( "The contest element " + element.getId() + " is not part of the contest "
          + getContest().getId() );

    GContestRound round = element.getContestRound();

    // Set the first round num if this is the first round the club played in
    //
    if ( d_nFirstRound == 0 )
      d_nFirstRound = round.getRoundNum();

    d_nLastRound = round.getRoundNum();

    if ( komatch.isWinner( getClub() ) )
    {
      setNumWonKOSeries( getNumWonKOSeries() + 1 );

      if ( round.isLastRound() )
      {
        setRank( 1 );
        setReachedRound( GContestRoundType.WINNER );
      }
    }
    else
    {
      setNumLostKOSeries( getNumLostKOSeries() + 1 );
      setRank( round.getBestPossibleLoserRank() );

      if ( getReachedRound() == GContestRoundType.NOT_DEFINED )
        setReachedRound( round.getRoundType() );
    }

    setLastRoundFormat( GContestRoundFormat.KNOCK_OUT );
    setNumMatches( getNumMatches() + komatch.getNumPlayedMatches() );
    setNumWins( getNumWins() + komatch.getNumWins( getClub() ) );
    setNumWinsOT( getNumWinsOT() + komatch.getNumWinsOT( getClub() ) );
    setNumLossesOT( getNumLossesOT() + komatch.getNumLossesOT( getClub() ) );
    setNumLosses( getNumLosses() + komatch.getNumLosses( getClub() ) );
    setNumPoints( getNumPoints() + komatch.getNumPoints( getClub() ) );
    updatePointsAvgPct();
    updateECRPoints( round );
  }

  /**
   * Removes the results of the given KO match which has been played as part of the also
   * given contest element.
   *
   * @param element the element in which a KO match has been played
   * @param komatch the played KO match
   */
  public void removeRoundResult( GKO3ContestElement element, GKOMatch komatch )
  {
    if ( !komatch.isPlayed() )
      throw new IllegalArgumentException( "The KOMatch " + komatch.getId() + " has not yet been played." );

    if ( !element.getContest().equals( getContest() ) )
      throw new IllegalArgumentException( "The contest element " + element.getId() + " is not part of the contest "
          + getContest().getId() );

    GContestRound round = element.getContestRound();
    GContestRound roundPrev = round.getContest().getPreviousRound( round );

    d_nLastRound = roundPrev.getRoundNum();

    if ( komatch.isWinner( getClub() ) )
      setNumWonKOSeries( getNumWonKOSeries() - 1 );
    else
      setNumLostKOSeries( getNumLostKOSeries() - 1 );

    setRank( 0 );
    setNumMatches( getNumMatches() - komatch.getNumPlayedMatches() );
    setNumWins( getNumWins() - komatch.getNumWins( getClub() ) );
    setNumWinsOT( getNumWinsOT() - komatch.getNumWinsOT( getClub() ) );
    setNumLossesOT( getNumLossesOT() - komatch.getNumLossesOT( getClub() ) );
    setNumLosses( getNumLosses() - komatch.getNumLosses( getClub() ) );
    setNumPoints( getNumPoints() - komatch.getNumPoints( getClub() ) );
    updatePointsAvgPct();
    updateECRPoints( round );
  }

  /**
   * <p>
   * Calculates the current ECR point value based on the number of played rounds, matches,
   * reached points etc. and sets the calculated value to the super class using the method
   * {@link #setPointsECR(BigDecimal)}.
   * </p>
   *
   * <p>
   * ECR points for a cup competition are calculated as follows:
   * </p>
   *
   * <ol>
   * <li>The total number of reached points is determined. For one KO round a maximum of 6
   * points is awarded even if three matches are played. I.e. a team winning the round in
   * three matches with two wins and one defeat earns 4 out of 6 possible points. A team
   * winning the round with two straight wins earns 6 out of 6 points.</li>
   * <li>For each won round, one bonus point is awarded.</li>
   * <li>All reached points are added and then divided by the maximum possible amount of
   * points. The maximum includes all potentially obtainable bonus points. The potentially
   * reachable amount is calculated as from the round, a team has entered the competition.
   * I.e. a team entering the competition in round 3 can reach less points then a team
   * entering the competition in round 1.</li>
   * <li>The quotient of the reached/possible points is then multiplied with the cup
   * competition coefficient. The result is the number of ECR points awarded for one cup
   * season.</li>
   * </ol>
   *
   * @param roundLastAdded the last added cup contest round
   */
  private void updateECRPoints( GContestRound roundLastAdded )
  {
    int nMaxNumRounds = roundLastAdded.getContest().getContestRounds().size() - d_nFirstRound + 1;

    // Actual points (based on minimum number of matches) + 1 bonus point for each won
    // round
    //
    int nMaxECRPoints = nMaxNumRounds + ( nMaxNumRounds * GMatchResult.VICTORY.getNumPoints() * 2 );

    int nNumMatchesActual = getNumMatches();
    int nNumMatchesIdeal = 2 * ( getNumWonKOSeries() + getNumLostKOSeries() );

    double dActualPoints = 0;
    if ( nNumMatchesActual > 0 )
      dActualPoints = ( ( getNumPoints() * nNumMatchesIdeal ) / (double) nNumMatchesActual ) + getNumWonKOSeries();

    double dECRPoints = dActualPoints / nMaxECRPoints;

    setPointsECR( new BigDecimal( dECRPoints ).multiply( getContest().getRankingCoefficient() ).setScale( 2,
        RoundingMode.HALF_UP ) );
  }

  /**
   * Returns the number of the first round the club entered the cup contest.
   */
  public int getFirstRound()
  {
    return d_nFirstRound;
  }

  /**
   * Returns the last round the club participated in.
   */
  public int getLastRound()
  {
    return d_nLastRound;
  }

  @Override
  public int compareTo( GClubHistoryRecord other )
  {
    int nTmp = super.compareTo( other );
    if ( nTmp != 0 )
      return nTmp;

    // Special cup criteria: lower first round (ASC)
    //
    return this.getFirstRound() - ( (GClubCupHistoryRecord) other).getFirstRound();
  }

  public Class<? extends IGEntity> getGEntityClass()
  {
    return GClubCupHistoryRecord.class;
  }
}
